/*
* Copyright (c) 2007 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

#ifndef M_REFPTR_H
#define M_REFPTR_H

#ifdef _MSC_VER
#pragma warning(disable:4127)
#endif

#include "ptrpolicy.h"

#include <stdexcept>

namespace Engine
{
	template<class, class, template<class> class>
    class RefPtr;

	class PtrUtils
    {
	public:
		template<class S1, class WP1, template<class> class DP1,class S, class WP, template<class> class DP>
		static bool IsEqual(const RefPtr<S1, WP1, DP1>& ptr, const RefPtr<S, WP, DP>& aptr);

		template<class S1, class WP1, template<class> class DP1,class S, class WP, template<class> class DP>
		static void Copy(RefPtr<S1, WP1, DP1>& ptr, const RefPtr<S, WP, DP>& aptr);
	};

    /**
	*   Class that represent reference counting pointer.
	*/
    template<class T,class WeakPolicy = NoWeakPtrExtension,template<class> class DeletePolicy = DefaultDelete>
    class RefPtr
    {
    public:

		friend class WeakUtils;
		friend class PtrUtils;

	    /**
		*   Default constructor.
		*/
        RefPtr():m_counter(0),m_pointer(0){}

		/**
		*   Constructor.
        *   Create new proxy object;
        */
        explicit RefPtr(T* ptr)
        {
			if(ptr != 0)
			{
#if defined(_DEBUG) && defined(_MSC_VER)
				m_counter = new(_CLIENT_BLOCK,__FILE__, __LINE__) Counter<WeakPolicy>();
#else
				m_counter = new Counter<WeakPolicy>();
#endif
				m_counter->AddRef();
				m_pointer = ptr;
			}
			else
			{
				m_counter = 0;
				m_pointer = 0;
			}
        }

		/**
		*   Copy constructor.
        *   Copy proxy object.
        */       
        RefPtr(const RefPtr& aptr)
        {
			PtrUtils::Copy(*this, aptr);
        }

		/**
		*   Generelized copy constructor.
        *   Copy proxy object.
        */
        template<class TT>
        RefPtr(const RefPtr<TT,WeakPolicy,DeletePolicy>& aptr)
        {
			PtrUtils::Copy(*this, aptr);
        }

		/**
		*   Destructor.
        */
        ~RefPtr()
        {
            Delete();
        }

		/**
		*   Operator assign one pointer to another.
        *   First kill prev value,that assign new.
        */       
        RefPtr& operator = (const RefPtr& rhs)
        {
			if(PtrUtils::IsEqual(*this,rhs))
                return *this;

			Delete();

			PtrUtils::Copy(*this, rhs);

            return *this;
        }

		/**
		*   Generelized operator assign one pointer to another.
        *   First kill prev value,that assign new.
        */
        template<class TT>
        RefPtr& operator = (const RefPtr<TT, WeakPolicy, DeletePolicy>& rhs)
        {
			if(PtrUtils::IsEqual(*this,rhs))
                return *this;

			Delete();

			PtrUtils::Copy(*this, rhs);

            return *this;
        }


		/**
		*   Return reference to real object using proxy object operator.
        */
        T& operator*()
        {
            if(m_counter != 0 && m_pointer != 0)
                return *m_pointer;
			else
				throw std::logic_error("Not initialized m_pointer");
        }

		/**
		*   Return m_pointer to real object using proxy object operator.
        */
        T* operator->()
        {
            if(m_counter != 0 && m_pointer != 0)
                return m_pointer;
			else
				std::logic_error("Not initialized m_pointer");
			return NULL;
        }

		/**
		*   Return reference to real object using proxy object operator.
        */
        const T& operator*()const
        {
            if(m_counter == 0 || m_pointer == 0)
				std::logic_error("Not initialized m_pointer");

			return *m_pointer;
        }

		/**
		*   Return m_pointer to real object using proxy object operator.
        */
        const T* operator->()const
        {
            if(m_counter == 0 || m_pointer == 0)
				std::logic_error("Not initialized m_pointer");
			return m_pointer;
        }

		/**
		*   Return m_pointer to object
		*/
		T* GetPointer()
		{
            if(m_counter == 0 || m_pointer == 0)
				std::logic_error("Not initialized m_pointer");
			return m_pointer;
		}

		/**
		*   Return m_pointer to object
		*/
		const T* GetPointer()const
		{
            if(m_counter == 0 || m_pointer == 0)
				std::logic_error("Not initialized m_pointer");
			return m_pointer;
		}

		/**
		*   Return number of references.
        */
        int GetCount()const
		{
			if(m_counter != 0 && m_pointer != 0)
				return m_counter->GetCount();
			return 0;
		};

		/**
		*   Test m_pointer is initialized or no.
        */
        bool IsNull()const
        {
            return (m_pointer == 0 && m_counter == 0);
        }

		/**
		*   Return true if there are reference to this pointer(weak also)
		*/
		bool IsReferenced()const
		{
			return ((m_counter->GetCount() > 1) || (m_counter->GetWeakCount() != 0));
		}


		/**
		*   Delete value.
        *   Decrement reference, and destroy object.
        */
        void Delete()
        {
            if(m_counter != 0 && m_pointer != 0)
            {
                m_counter->DelRef();

				if(m_counter->GetCount() == 0)
					DeletePolicy<T>::Delete(&m_pointer);

                if(m_counter->GetWeakCount() == 0 && m_counter->GetCount() == 0)
					delete m_counter;

				m_counter = 0;
				m_pointer = 0;
            }
        }

		/**
		*   Comparison operator.
        *   Compare pointers to proxy objects.
        */
        bool operator == ( const RefPtr<T, WeakPolicy, DeletePolicy>& ptr )
        {
			return PtrUtils::IsEqual(*this, ptr);
        }

		/**
		*   Comparison operator.
        *   Compare pointers to proxy objects.
        */
        bool operator != ( const RefPtr<T, WeakPolicy, DeletePolicy>& ptr )
        {
            return !PtrUtils::IsEqual(*this, ptr);
        }

	private:		

        Counter<WeakPolicy>* m_counter;
		T*       m_pointer;
    };
	/********************************************************************************************************/
	template<class S1, class WP1, template<class> class DP1,class S, class WP, template<class> class DP>
	static bool PtrUtils::IsEqual(const RefPtr<S1, WP1, DP1>& ptr, const RefPtr<S, WP, DP>& aptr)
	{
		return (ptr.m_counter == aptr.m_counter && ptr.m_pointer == aptr.m_pointer);
	}
	/********************************************************************************************************/
	template<class S1, class WP1, template<class> class DP1,class S, class WP, template<class> class DP>
	void PtrUtils::Copy(RefPtr<S1, WP1, DP1>& ptr, const RefPtr<S, WP, DP>& aptr)
    {
        ptr.m_counter = aptr.m_counter;
		ptr.m_pointer = aptr.m_pointer;
        if(ptr.m_counter != 0)
			ptr.m_counter->AddRef();
    }
	/********************************************************************************************************/
}
#endif
